{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# Pandas 数据类型转换",
        "",
        "本教程详细介绍如何在 Pandas 中转换数据类型。",
        "",
        "## 目录",
        "1. 查看数据类型",
        "2. 使用 astype() 转换类型",
        "3. 使用 to_numeric() 转换数值",
        "4. 使用 to_datetime() 转换日期",
        "5. 使用 to_timedelta() 转换时间差",
        "6. 使用 astype('category') 转换分类",
        "7. 高级技巧"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 导入库"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "import pandas as pd",
        "import numpy as np"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 1. 查看数据类型",
        "",
        "### 方法说明",
        "",
        "了解数据类型是转换的前提。",
        "",
        "**主要方法：**",
        "- `df.dtypes`: 查看每列的数据类型",
        "- `df.info()`: 查看数据类型和内存使用",
        "- `df.select_dtypes()`: 选择特定类型的列",
        "- `Series.dtype`: 查看单个列的类型",
        "",
        "**适用场景：** 数据探索阶段，了解数据类型"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 示例1: 创建示例数据"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "df = pd.DataFrame({",
        "    'ID': [1, 2, 3, 4, 5],",
        "    '姓名': ['张三', '李四', '王五', '赵六', '钱七'],",
        "    '年龄': ['25', '30', '35', '28', '32'],  # 字符串格式",
        "    '工资': [8000, 12000, 15000, 6000, 9000],",
        "    '入职日期': ['2020-01-01', '2019-06-15', '2021-03-20', '2020-12-01', '2021-05-10'],",
        "    '部门': ['技术', '销售', '技术', '人事', '销售']",
        "})",
        "",
        "print(\"原始 DataFrame:\")",
        "print(df)",
        "print(\"\\n数据类型:\")",
        "print(df.dtypes)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 示例2: 使用 info() 查看详细信息"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "df.info()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 示例3: 选择特定类型的列"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "print(\"选择数值型列:\")",
        "print(df.select_dtypes(include=['int64', 'float64']))",
        "",
        "print(\"\\n选择字符串类型列:\")",
        "print(df.select_dtypes(include=['object']))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 2. 使用 astype() 转换类型",
        "",
        "### 方法说明",
        "",
        "`astype()` 是最常用的类型转换方法。",
        "",
        "**语法:**",
        "```python",
        "df.astype(dtype, copy=True, errors='raise')",
        "```",
        "",
        "**主要参数：**",
        "- `dtype`: 目标数据类型",
        "- `copy`: 是否返回副本（默认 True）",
        "- `errors`: 'raise' 出错时抛出异常，'ignore' 忽略错误",
        "",
        "**支持的类型：**",
        "- 数值类型: int, float, int64, float64",
        "- 字符串类型: str, 'object'",
        "- 布尔类型: bool",
        "- 分类类型: 'category'",
        "- 日期类型: 'datetime64[ns]'",
        "",
        "**特点：**",
        "- ✅ 可以转换单个列或所有列",
        "- ✅ 支持字典指定不同列的类型",
        "- ✅ 功能强大，支持多种类型",
        "",
        "**适用场景：** 明确的类型转换，知道数据格式正确"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 示例1: 转换单个列"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "print(\"原始数据:\")",
        "print(df['年龄'].dtype)",
        "print(df['年龄'])",
        "",
        "print(\"\\n转换为整数:\")",
        "df['年龄'] = df['年龄'].astype(int)",
        "print(df['年龄'].dtype)",
        "print(df['年龄'])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 示例2: 转换多个列"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "print(\"原始数据类型:\")",
        "print(df[['年龄', '工资']].dtypes)",
        "",
        "# 转换多列",
        "df[['年龄', '工资']] = df[['年龄', '工资']].astype(float)",
        "print(\"\\n转换后:\")",
        "print(df[['年龄', '工资']].dtypes)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 示例3: 使用字典指定不同列的类型"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "df_copy = df.copy()",
        "# 使用字典为不同列指定不同类型",
        "df_converted = df_copy.astype({",
        "    'ID': 'int64',",
        "    '年龄': 'float64',",
        "    '工资': 'float64'",
        "})",
        "",
        "print(\"转换后的数据类型:\")",
        "print(df_converted.dtypes)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 示例4: 转换为字符串类型"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "df_copy = df.copy()",
        "df_copy['ID'] = df_copy['ID'].astype(str)",
        "print(\"ID 转换为字符串:\")",
        "print(df_copy['ID'].dtype)",
        "print(df_copy['ID'].head())"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 示例5: 转换布尔类型"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "df_bool = pd.DataFrame({",
        "    '成绩': [85, 90, 78, 65, 92],",
        "    '是否及格': [1, 1, 1, 0, 1]  # 0/1 格式",
        "})",
        "",
        "print(\"原始数据:\")",
        "print(df_bool)",
        "",
        "# 转换为布尔类型",
        "df_bool['是否及格'] = df_bool['是否及格'].astype(bool)",
        "print(\"\\n转换后:\")",
        "print(df_bool)",
        "print(\"\\n数据类型:\", df_bool['是否及格'].dtype)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 3. 使用 to_numeric() 转换数值",
        "",
        "### 方法说明",
        "",
        "`to_numeric()` 专门用于转换为数值类型，比 `astype()` 更智能。",
        "",
        "**语法:**",
        "```python",
        "pd.to_numeric(arg, errors='raise', downcast=None)",
        "```",
        "",
        "**主要参数：**",
        "- `arg`: 要转换的数据（Series、列表等）",
        "- `errors`: 'raise' 抛出异常，'coerce' 转换为 NaN，'ignore' 保持原样",
        "- `downcast`: 自动选择最小的数值类型",
        "",
        "**特点：**",
        "- ✅ 可以处理错误数据（转换为 NaN）",
        "- ✅ 自动识别数字字符串",
        "- ✅ 支持自动选择数据类型（downcast）",
        "",
        "**适用场景：** 转换字符串数字，或数据中可能有错误值时"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 示例1: 转换字符串数字"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "df_num = pd.DataFrame({",
        "    '价格': ['100', '200', '300', '400', '500'],",
        "    '数量': ['10.5', '20.3', '30.7', '40.2', '50.9']",
        "})",
        "",
        "print(\"原始数据:\")",
        "print(df_num)",
        "print(\"\\n数据类型:\", df_num.dtypes)",
        "",
        "# 转换为数值",
        "df_num['价格'] = pd.to_numeric(df_num['价格'])",
        "df_num['数量'] = pd.to_numeric(df_num['数量'])",
        "",
        "print(\"\\n转换后:\")",
        "print(df_num)",
        "print(\"\\n数据类型:\", df_num.dtypes)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 示例2: 处理错误数据 (errors参数)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "df_error = pd.DataFrame({",
        "    '数值': ['100', '200', 'abc', '400', '500']  # 包含非数字",
        "})",
        "",
        "print(\"原始数据:\")",
        "print(df_error)",
        "",
        "print(\"\\n使用 errors='raise' (默认，会报错):\")",
        "try:",
        "    pd.to_numeric(df_error['数值'], errors='raise')",
        "except Exception as e:",
        "    print(f\"错误: {e}\")",
        "",
        "print(\"\\n使用 errors='coerce' (转换为 NaN):\")",
        "df_error['数值'] = pd.to_numeric(df_error['数值'], errors='coerce')",
        "print(df_error)",
        "",
        "print(\"\\n使用 errors='ignore' (保持原样):\")",
        "df_ignore = pd.DataFrame({'数值': ['100', '200', 'abc', '400', '500']})",
        "df_ignore['数值'] = pd.to_numeric(df_ignore['数值'], errors='ignore')",
        "print(df_ignore)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 示例3: 使用 downcast 自动选择类型"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "df_down = pd.DataFrame({",
        "    '整数': ['1', '2', '3', '4', '5'],",
        "    '浮点数': ['1.5', '2.5', '3.5', '4.5', '5.5']",
        "})",
        "",
        "print(\"原始数据类型:\")",
        "print(df_down.dtypes)",
        "",
        "# 自动选择最小的数值类型",
        "df_down['整数'] = pd.to_numeric(df_down['整数'], downcast='integer')",
        "df_down['浮点数'] = pd.to_numeric(df_down['浮点数'], downcast='float')",
        "",
        "print(\"\\n使用 downcast 后:\")",
        "print(df_down.dtypes)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 4. 使用 to_datetime() 转换日期",
        "",
        "### 方法说明",
        "",
        "`to_datetime()` 用于转换为日期时间类型。",
        "",
        "**语法:**",
        "```python",
        "pd.to_datetime(arg, format=None, errors='raise', dayfirst=False, yearfirst=False)",
        "```",
        "",
        "**主要参数：**",
        "- `arg`: 要转换的数据",
        "- `format`: 日期格式字符串（如 '%Y-%m-%d'）",
        "- `errors`: 'raise' 抛出异常，'coerce' 转换为 NaT，'ignore' 保持原样",
        "- `dayfirst`: 日期在月份前（欧洲格式）",
        "- `yearfirst`: 年份在日期前",
        "",
        "**特点：**",
        "- ✅ 自动识别多种日期格式",
        "- ✅ 可以指定日期格式",
        "- ✅ 处理错误数据",
        "",
        "**适用场景：** 转换日期字符串为日期类型"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 示例1: 自动识别日期格式"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "df_date = pd.DataFrame({",
        "    '日期1': ['2024-01-01', '2024-02-15', '2024-03-20'],",
        "    '日期2': ['01/15/2024', '02/20/2024', '03/25/2024'],",
        "    '日期3': ['2024-01-01 10:30:00', '2024-02-15 14:20:00', '2024-03-20 16:45:00']",
        "})",
        "",
        "print(\"原始数据:\")",
        "print(df_date)",
        "print(\"\\n数据类型:\", df_date.dtypes)",
        "",
        "# 自动识别格式",
        "df_date['日期1'] = pd.to_datetime(df_date['日期1'])",
        "df_date['日期2'] = pd.to_datetime(df_date['日期2'])",
        "df_date['日期3'] = pd.to_datetime(df_date['日期3'])",
        "",
        "print(\"\\n转换后:\")",
        "print(df_date)",
        "print(\"\\n数据类型:\", df_date.dtypes)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 示例2: 指定日期格式"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "df_format = pd.DataFrame({",
        "    '日期': ['20240101', '20240215', '20240320']  # YYYYMMDD 格式",
        "})",
        "",
        "print(\"原始数据:\")",
        "print(df_format)",
        "",
        "# 指定格式",
        "df_format['日期'] = pd.to_datetime(df_format['日期'], format='%Y%m%d')",
        "print(\"\\n指定格式转换后:\")",
        "print(df_format)",
        "print(\"\\n数据类型:\", df_format['日期'].dtype)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 示例3: 处理错误日期数据"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "df_error_date = pd.DataFrame({",
        "    '日期': ['2024-01-01', '2024-02-15', 'invalid', '2024-03-20']",
        "})",
        "",
        "print(\"原始数据:\")",
        "print(df_error_date)",
        "",
        "# 使用 errors='coerce' 将错误数据转换为 NaT",
        "df_error_date['日期'] = pd.to_datetime(df_error_date['日期'], errors='coerce')",
        "print(\"\\n转换后 (错误数据变为 NaT):\")",
        "print(df_error_date)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 5. 使用 to_timedelta() 转换时间差",
        "",
        "### 方法说明",
        "",
        "`to_timedelta()` 用于转换为时间差类型。",
        "",
        "**语法:**",
        "```python",
        "pd.to_timedelta(arg, unit=None, errors='raise')",
        "```",
        "",
        "**主要参数：**",
        "- `arg`: 要转换的数据",
        "- `unit`: 时间单位（'D' 天，'H' 小时，'M' 分钟，'S' 秒等）",
        "- `errors`: 'raise' 抛出异常，'coerce' 转换为 NaT，'ignore' 保持原样",
        "",
        "**适用场景：** 转换时间差数据"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 示例: 转换时间差"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "df_timedelta = pd.DataFrame({",
        "    '天数': [1, 2, 3, 4, 5],",
        "    '小时数': [24, 48, 72, 96, 120]",
        "})",
        "",
        "print(\"原始数据:\")",
        "print(df_timedelta)",
        "",
        "# 转换为时间差",
        "df_timedelta['天数'] = pd.to_timedelta(df_timedelta['天数'], unit='D')",
        "df_timedelta['小时数'] = pd.to_timedelta(df_timedelta['小时数'], unit='H')",
        "",
        "print(\"\\n转换后:\")",
        "print(df_timedelta)",
        "print(\"\\n数据类型:\", df_timedelta.dtypes)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 6. 使用 astype('category') 转换分类",
        "",
        "### 方法说明",
        "",
        "分类类型（category）可以节省内存并提高性能。",
        "",
        "**特点：**",
        "- ✅ 节省内存（特别是重复值多的列）",
        "- ✅ 提高性能（排序、分组等）",
        "- ✅ 可以指定顺序",
        "",
        "**适用场景：** 类别型数据，值重复较多时"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 示例1: 转换为分类类型"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "df_cat = pd.DataFrame({",
        "    '部门': ['技术', '销售', '技术', '人事', '销售', '技术', '人事'],",
        "    '等级': ['A', 'B', 'A', 'C', 'B', 'A', 'C']",
        "})",
        "",
        "print(\"原始数据:\")",
        "print(df_cat)",
        "print(\"\\n内存使用:\")",
        "print(df_cat.memory_usage(deep=True))",
        "",
        "# 转换为分类类型",
        "df_cat['部门'] = df_cat['部门'].astype('category')",
        "df_cat['等级'] = df_cat['等级'].astype('category')",
        "",
        "print(\"\\n转换后:\")",
        "print(df_cat)",
        "print(\"\\n数据类型:\", df_cat.dtypes)",
        "print(\"\\n内存使用:\")",
        "print(df_cat.memory_usage(deep=True))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 示例2: 指定分类顺序"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "df_order = pd.DataFrame({",
        "    '等级': ['高', '中', '低', '高', '中', '低']",
        "})",
        "",
        "# 转换为有序分类",
        "df_order['等级'] = pd.Categorical(",
        "    df_order['等级'],",
        "    categories=['低', '中', '高'],",
        "    ordered=True",
        ")",
        "",
        "print(\"原始数据:\")",
        "print(df_order)",
        "",
        "print(\"\\n转换为有序分类后:\")",
        "print(df_order)",
        "print(\"\\n可以进行排序比较:\")",
        "print(df_order[df_order['等级'] > '低'])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 7. 高级技巧",
        "",
        "### 技巧1: 批量转换所有数值列"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "df_mix = pd.DataFrame({",
        "    'A': ['1', '2', '3'],",
        "    'B': ['10.5', '20.5', '30.5'],",
        "    'C': ['a', 'b', 'c']",
        "})",
        "",
        "print(\"原始数据:\")",
        "print(df_mix)",
        "",
        "# 尝试将所有列转换为数值，不能转换的保持原样",
        "for col in df_mix.columns:",
        "    df_mix[col] = pd.to_numeric(df_mix[col], errors='ignore')",
        "",
        "print(\"\\n批量转换后:\")",
        "print(df_mix)",
        "print(\"\\n数据类型:\", df_mix.dtypes)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 技巧2: 转换后验证数据"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "df_verify = pd.DataFrame({",
        "    '年龄': ['25', '30', '35', '28', '32']",
        "})",
        "",
        "print(\"原始数据:\")",
        "print(df_verify)",
        "",
        "# 转换并验证",
        "df_verify['年龄'] = pd.to_numeric(df_verify['年龄'], errors='coerce')",
        "",
        "print(\"\\n转换后:\")",
        "print(df_verify)",
        "",
        "# 检查是否有 NaN",
        "if df_verify['年龄'].isna().any():",
        "    print(\"\\n警告: 转换后存在缺失值!\")",
        "    print(df_verify[df_verify['年龄'].isna()])",
        "else:",
        "    print(\"\\n转换成功，无缺失值!\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 技巧3: 链式转换"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "df_chain = pd.DataFrame({",
        "    'ID': ['1', '2', '3', '4', '5']",
        "})",
        "",
        "print(\"原始数据:\")",
        "print(df_chain)",
        "",
        "# 链式转换：字符串 -> 数值 -> 整数",
        "df_chain['ID'] = pd.to_numeric(df_chain['ID']).astype(int)",
        "",
        "print(\"\\n链式转换后:\")",
        "print(df_chain)",
        "print(\"\\n数据类型:\", df_chain['ID'].dtype)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 总结",
        "",
        "### 方法对比",
        "",
        "| 方法 | 用途 | 优点 | 缺点 |",
        "|------|------|------|------|",
        "| `astype()` | 通用类型转换 | 功能全面，速度快 | 不能处理错误数据 |",
        "| `to_numeric()` | 转换为数值 | 可以处理错误，智能 | 只能转换数值 |",
        "| `to_datetime()` | 转换为日期 | 自动识别格式 | 只能转换日期 |",
        "| `astype('category')` | 转换为分类 | 节省内存，提高性能 | 不适合唯一值多的列 |",
        "",
        "### 关键要点",
        "",
        "1. **先查看类型**：使用 `dtypes` 或 `info()` 了解数据类型",
        "2. **选择合适的转换方法**：根据数据特点选择 `astype()` 或专用方法",
        "3. **处理错误数据**：使用 `errors='coerce'` 将错误数据转换为 NaN/NaT",
        "4. **验证转换结果**：转换后检查是否有缺失值或异常",
        "5. **使用分类类型**：类别数据使用 `category` 可以节省内存",
        "6. **指定格式**：日期转换时最好指定格式，避免歧义",
        "7. **批量转换**：使用循环或字典批量转换多列"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "name": "python",
      "version": "3.8.0"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 4
}